---
title: "AbortSignal / Timeouts"
description: "Cancel in-flight LLM operations to save time and resources"
---

## Overview

Abort controllers allow you to cancel ongoing LLM operations, which is essential for:
- User-initiated cancellations (e.g., "Stop generating" buttons)
- Implementing timeouts for long-running operations
- Cleaning up resources when components unmount
- Managing multiple parallel requests

## Quick Start

<Tabs>
  <Tab title="TypeScript" language="typescript">
    ```typescript
    import { b } from '@/baml_client'

    // TypeScript uses AbortSignal for cancellation
    // No additional imports needed - it's built into the runtime

    // Modern approach: Use AbortSignal.timeout() for automatic timeout
    try {
      const result = await b.ExtractResume(text, {
        signal: AbortSignal.timeout(5000) // 5 second timeout
      })
    } catch (error) {
      if (error.name === 'BamlAbortError') {
        console.log('Operation was cancelled')
      }
    }

    // Manual approach: Create controller and cancel later
    const controller = new AbortController()
    const promise = b.ExtractResume(text, {
      signal: controller.signal
    })

    // Cancel after 5 seconds
    setTimeout(() => controller.abort(), 5000)

    try {
      const result = await promise
    } catch (error) {
      if (error.name === 'BamlAbortError') {
        console.log('Operation was cancelled')
      }
    }
    ```
  </Tab>
  
  <Tab title="Python" language="python">
    ```python
    from baml_client import b
    # Python doesn't have a native abort controller construct,
    # so BAML provides a custom implementation
    from baml_py import AbortController
    import asyncio

    # Will cancel after 5 seconds, once its used.
    controller = AbortController(timeout_ms=5000)
    # one can also manually call abort:
    controller.abort()
    # once aborted, the controller will forever remain in an an aborted state.

    async def run_with_timeout():        
        try:
            result = await b.ExtractResume(
                text,
                baml_options={"abort_controller": controller}
            )
        except BamlAbortError:
            print("Operation was cancelled")
    ```
  </Tab>
  
  <Tab title="Go" language="go">
    ```go
    import (
        "context"
        "time"
    )

    // Go uses the standard context.Context for cancellation
    // This is the idiomatic Go way to handle cancellation and timeouts
    // Create context with 5 second timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    result, err := b.ExtractResume(ctx, text)
    if err != nil {
        if errors.Is(err, context.DeadlineExceeded) {
            fmt.Println("Operation timed out")
        } else if errors.Is(err, context.Canceled) {
            fmt.Println("Operation was cancelled")
        }
    }
    ```
  </Tab>
</Tabs>

## Basic Examples

### Implementing Timeouts

Automatically cancel operations that take too long:

<Tabs>
  <Tab title="TypeScript" language="typescript">
    ```typescript
    // Modern approach using AbortSignal.timeout()
    async function extractWithTimeout(text: string, timeoutMs: number = 30000) {
      try {
        const result = await b.ExtractResume(text, {
          signal: AbortSignal.timeout(timeoutMs)
        })
        return result
      } catch (error) {
        if (error.name === 'BamlAbortError') {
          throw new Error(`Operation timed out after ${timeoutMs}ms`)
        }
        throw error
      }
    }

    // Manual implementation (for when you need more control)
    async function extractWithManualTimeout(text: string, timeoutMs: number = 30000) {
      const controller = new AbortController()
      
      // Set up automatic timeout
      const timeoutId = setTimeout(() => {
        controller.abort('timeout')
      }, timeoutMs)

      try {
        const result = await b.ExtractResume(text, {
          signal: controller.signal
        })
        clearTimeout(timeoutId)
        return result
      } catch (error) {
        clearTimeout(timeoutId)
        if (error.name === 'BamlAbortError') {
          throw new Error(`Operation timed out after ${timeoutMs}ms`)
        }
        throw error
      }
    }
    ```
  </Tab>
  
  <Tab title="Python" language="python">
    ```python
    import asyncio
    from baml_py import AbortController

    async def extract_with_timeout(text: str, timeout_seconds: float = 30):
        controller = AbortController()
        
        async def timeout_task():
            await asyncio.sleep(timeout_seconds)
            controller.abort()
        
        # Start timeout
        timeout = asyncio.create_task(timeout_task())
        
        try:
            result = await b.ExtractResume(
                text,
                baml_options={"abort_controller": controller}
            )
            timeout.cancel()
            return result
        except BamlAbortError:
            raise TimeoutError(f"Operation timed out after {timeout_seconds}s")
        except Exception:
            timeout.cancel()
            raise
    ```
  </Tab>
  
  <Tab title="Go" language="go">
    ```go
    func extractWithTimeout(text string, timeout time.Duration) (Result, error) {
        ctx, cancel := context.WithTimeout(context.Background(), timeout)
        defer cancel()
        
        result, err := b.ExtractResume(ctx, text)
        if err != nil {
            if errors.Is(err, context.DeadlineExceeded) {
                return nil, fmt.Errorf("operation timed out after %v", timeout)
            }
            return nil, err
        }
        
        return result, nil
    }
    ```
  </Tab>
</Tabs>

### User-Initiated Cancellation

Build responsive backend services that allow users to cancel long-running operations:

<Tabs>
  <Tab title="TypeScript (Express)" language="typescript">
    ```typescript
    import express from 'express'
    import { b } from '@/baml_client'
    
    const app = express()
    const activeControllers = new Map<string, AbortController>()
    
    app.post('/extract/:requestId', async (req, res) => {
      const { requestId } = req.params
      const { text } = req.body
      
      const controller = new AbortController()
      activeControllers.set(requestId, controller)
      
      try {
        const result = await b.ExtractResume(text, {
          signal: controller.signal
        })
        res.json({ result })
      } catch (error) {
        if (error.name === 'BamlAbortError') {
          res.json({ status: 'cancelled' })
        } else {
          res.status(500).json({ error: error.message })
        }
      } finally {
        activeControllers.delete(requestId)
      }
    })
    
    app.post('/cancel/:requestId', (req, res) => {
      const { requestId } = req.params
      const controller = activeControllers.get(requestId)
      
      if (controller) {
        controller.abort()
        res.json({ status: 'cancellation requested' })
      } else {
        res.status(404).json({ status: 'request not found' })
      }
    })
    ```
  </Tab>
  
  <Tab title="Python (FastAPI)" language="python">
    ```python
    from fastapi import FastAPI, BackgroundTasks
    from baml_py import AbortController
    import asyncio

    app = FastAPI()
    active_controllers = {}

    @app.post("/extract/{request_id}")
    async def extract_resume(request_id: str, text: str):
        controller = AbortController()
        active_controllers[request_id] = controller
        
        try:
            result = await b.ExtractResume(
                text,
                baml_options={"abort_controller": controller}
            )
            return {"result": result}
        except BamlAbortError:
            return {"status": "cancelled"}
        finally:
            active_controllers.pop(request_id, None)

    @app.post("/cancel/{request_id}")
    async def cancel_extraction(request_id: str):
        if controller := active_controllers.get(request_id):
            controller.abort()
            return {"status": "cancellation requested"}
        return {"status": "request not found"}
    ```
  </Tab>
</Tabs>

## Streaming with Abort Controllers

Abort controllers work seamlessly with streaming responses:

<Tabs>
  <Tab title="TypeScript" language="typescript">
    ```typescript
    const controller = new AbortController()

    const stream = b.stream.GenerateStory(prompt, {
      signal: controller.signal
    })

    let wordCount = 0
    try {
      for await (const chunk of stream) {
        wordCount += chunk.split(' ').length
        
        // Stop if we've generated enough
        if (wordCount > 1000) {
          controller.abort('word limit reached')
          break
        }
        
        // Process chunk
        console.log(chunk)
      }
    } catch (error) {
      if (error instanceof BamlAbortError) {
        console.log('Stream cancelled:', error.reason)
      }
    }
    ```
  </Tab>
  
  <Tab title="Python" language="python">
    ```python
    controller = AbortController()
    
    stream = b.stream.GenerateStory(
        prompt,
        baml_options={"abort_controller": controller}
    )
    
    word_count = 0
    async for chunk in stream:
        word_count += len(chunk.split())
        
        # Stop if we've generated enough
        if word_count > 1000:
            controller.abort()
            break
        
        # Process chunk
        print(chunk)
    ```
  </Tab>
  
  <Tab title="Go" language="go">
    ```go
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    stream := b.StreamGenerateStory(ctx, prompt)
    
    wordCount := 0
    for chunk := range stream {
        wordCount += len(strings.Fields(chunk))
        
        // Stop if we've generated enough
        if wordCount > 1000 {
            cancel()
            break
        }
        
        // Process chunk
        fmt.Println(chunk)
    }
    ```
  </Tab>
</Tabs>

## Error Handling

Properly handle abort errors to distinguish cancellations from other failures:

<Tabs>
  <Tab title="TypeScript" language="typescript">
    ```typescript
    import { BamlAbortError } from '@/baml_client'

    try {
      const result = await b.ExtractResume(text, {
        signal: controller.signal
      })
      return { success: true, data: result }
    } catch (error) {
      if (error instanceof BamlAbortError) {
        // User cancelled - this is expected
        return { success: false, cancelled: true }
      }
      
      if (error.name === 'BamlValidationError') {
        // Schema validation failed
        return { success: false, validationError: error.message }
      }
      
      // Unexpected error
      console.error('Extraction failed:', error)
      throw error
    }
    ```
  </Tab>
  
  <Tab title="Python" language="python">
    ```python
    from baml_py import BamlAbortError, BamlValidationError

    try:
        result = await b.ExtractResume(
            text,
            baml_options={"abort_controller": controller}
        )
        return {"success": True, "data": result}
        
    except BamlAbortError:
        # User cancelled - this is expected
        return {"success": False, "cancelled": True}
        
    except BamlValidationError as e:
        # Schema validation failed
        return {"success": False, "validation_error": str(e)}
        
    except Exception as e:
        # Unexpected error
        logger.error(f"Extraction failed: {e}")
        raise
    ```
  </Tab>
  
  <Tab title="Go" language="go">
    ```go
    result, err := b.ExtractResume(ctx, text)
    if err != nil {
        if errors.Is(err, context.Canceled) {
            // User cancelled - this is expected
            return Result{Success: false, Cancelled: true}, nil
        }
        
        if errors.Is(err, context.DeadlineExceeded) {
            // Timeout occurred
            return Result{Success: false, TimedOut: true}, nil
        }
        
        // Other error
        return Result{}, fmt.Errorf("extraction failed: %w", err)
    }
    
    return Result{Success: true, Data: result}, nil
    ```
  </Tab>
</Tabs>

## Best Practices

### When to Use Each Pattern

<Tabs>
  <Tab title="TypeScript" language="typescript">
    ```typescript
    // ✅ Use AbortSignal.timeout() for simple timeouts
    const result = await b.ExtractResume(text, {
      signal: AbortSignal.timeout(30000)
    })
    
    // ✅ Use manual AbortController when you need to cancel conditionally
    const controller = new AbortController()
    const promise = b.ExtractResume(text, {
      signal: controller.signal
    })
    
    // Cancel based on user action or business logic
    if (shouldCancel) {
      controller.abort('cancelled by user')
    }
    
    // ✅ Combine both patterns for timeout + manual control
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort('timeout'), 30000)
    
    const result = await b.ExtractResume(text, {
      signal: controller.signal
    })
    
    clearTimeout(timeoutId)
    ```
  </Tab>
</Tabs>

### Key Benefits

- **AbortSignal.timeout()**: Cleaner code for simple timeout scenarios
- **Manual AbortController**: More control over cancellation logic and reasons
- **Better Error Handling**: Clear distinction between timeouts and user cancellations
- **Standards Compliance**: Uses modern web standards that work across different environments

## Advanced Patterns

For more advanced abort controller patterns including:
- **Cancelling parallel operations** - Cancel multiple concurrent calls at once or individually
- **Fastest request wins** - Race multiple LLM providers and cancel slower ones
- **Implementing timeouts for parallel operations** - Set automatic timeouts for batches of operations
- **Batching with cancellation support** - Process items in batches with cancellation

See the [Concurrent Calls guide](/guide/baml-basics/concurrent-calls#cancelling-parallel-operations) for detailed examples and implementations.

## Related Topics

- [Error Handling](/guide/baml-basics/error-handling) - Learn about all error types including BamlAbortError
- [Streaming](/guide/baml-basics/streaming#cancelling-streams) - Stream responses with cancellation support
- [Concurrent Calls](/guide/baml-basics/concurrent-calls) - Advanced cancellation patterns for parallel operations
- [API Reference](/ref/baml_client/abort-signal) - Detailed API documentation